package com.zyf.concurrency.chapter14;

import com.zyf.concurrency.annotations.ThreadSafe;

/**
 * 使用简单阻塞实现的有界缓存
 * 引自原文：
 *      程序清单14-5中的SleepyBoundedBuffer尝试通过put和take方法来实现一种简单的“轮
 * 询与休眠”重试机制，从而使调用者无须在每次调用时都实现重试逻辑。如果缓存为空，那么
 * take将休眠并直到另一个线程在缓存中放人一些数据;如果缓存是满的，那么put将休眠并直
 * 到另一个线程从缓存中移除一些数据， 以便有空间容纳新的数据。这种方法将前提条件的管理
 * 操作封装起来，并简化了对缓存的使用--这正是朝着正确的改进方向迈出了一步。
 *      Queue提供了上述两种选择，即poll方法能够在队列为空时返回null,而remove方法则抛出一个异常，但
 * Queue并不适合在生产者--消费者设计中使用。BlockingQueue 中的操作只有当队列处于正确状态时才会进
 * 行处理，否则将阻塞，因此当生产者和消费者并发执行时，BlockingQueue 才是更好的选择。
 * create by yifeng
 */
@ThreadSafe
public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer<V> {

    int SLEEP_GRANULARITY = 60;

    protected SleepyBoundedBuffer() {
        super(100);
    }

    protected SleepyBoundedBuffer(int capacity) {
        super(capacity);
    }

    public void put(V v) throws InterruptedException {
        while (true) {
            synchronized (this) {
                if(!isFull()) {
                    doPut(v);
                    return;
                }
            }
            Thread.sleep(SLEEP_GRANULARITY);
        }
    }

    public V take() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (!isEmpty()) {
                    return doTake();
                }
            }
            //通常，如果线程在休眠或者被阻塞时持有一个锁，那么这通常是一种不好的做法，因为只要线程不释放这个
            //锁，有些条件(缓存为满/空)就永远无法为真。
            Thread.sleep(SLEEP_GRANULARITY);
        }
    }

}
